昨天我們完成了 BluetoothService 的藍牙核心邏輯,讓 App 可以進行掃描、連線並接收藍牙數據。
今天要更進一步,把這些功能與畫面串接起來,讓使用者能清楚地看到附近的藍牙裝置,並能點擊連接設備。
在前一篇中,我們在 BluetoothService 定義了一個協議:
protocol BluetoothServiceDelegate: AnyObject {
func getBLEPeripherals(peripherals: [CBPeripheral]) // 回傳掃描到的裝置
func getBLEPeripheralsValue(value: String) // 回傳接收到的數據
}
這個 delegate 是 藍牙核心與畫面之間的橋樑。
當 Service 掃描到新裝置或接收到資料時,會透過這個協議回傳給 UI。
而今天的 MainViewController 就會負責實作這個協議,讓畫面能即時反映藍牙狀態。
先來看看控制器的架構:
class MainViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var lbLightNumber: UILabel!
private var peripherals: [CBPeripheral] = []
private var connectedPeripheral: CBPeripheral?
override func viewDidLoad() {
super.viewDidLoad()
setUI()
BluetoothService.shared.delegate = self // 關鍵:設定代理
}
}
這裡主要做了兩件事:
peripherals 陣列,存放掃描到的藍牙裝置。viewDidLoad 裡設定代理 (delegate),確保藍牙事件能即時傳回 UI。接著,使用 UITableView 來顯示掃描到的藍牙清單:
extension MainViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return peripherals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BluetoothTableViewCell", for: indexPath) as? BluetoothTableViewCell else {
return UITableViewCell()
}
let peripheral = peripherals[indexPath.row]
cell.lbName.text = peripheral.name ?? "未知設備"
return cell
}
}
這樣就能在畫面上即時顯示掃描到的藍牙裝置名稱。
當使用者點擊其中一個項目時:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let selectedPeripheral = peripherals[indexPath.row]
BluetoothService.shared.connectPeripheral(peripheral: selectedPeripheral)
}
App 會呼叫 BluetoothService 去嘗試連線該設備。
接著實作 BluetoothServiceDelegate,處理掃描與資料更新事件:
extension MainViewController: BluetoothServiceDelegate {
func getBLEPeripherals(peripherals: [CBPeripheral]) {
self.peripherals = peripherals
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
func getBLEPeripheralsValue(value: String) {
DispatchQueue.main.async {
self.lbLightNumber.text = "光線強度:\(value)"
}
}
}
這裡有兩個關鍵重點:
DispatchQueue.main.async)。getBLEPeripheralsValue 即時顯示裝置傳來的數據,例如感測器的光線值或其他量測資訊。今天我們完成了整個「藍牙掃描 → UI 顯示 → 連線 → 數據更新」的流程:
到這一步,我們的藍牙 App 已經具備完整的互動性與基本資料傳輸能力 🎉
明天(Day29),我們將深入探討 藍牙數據傳輸與解析機制,包含: